package pcsd.server;

import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

import pcsd.worker.JobProcessingWorker;

/**
 * Runs the simple server.
 * 
 * @author Benjamin Ertl, Justinas Murzinas, Boyan Mihaylov
 */
public class SimpleJobQueueServerRunner {

	/** The number of workers. */
	private static int numWorkers = 1;

	/** The default maximum size of each queue per worker. */
	private static int maxDequeSize = 10;
	
	/**
	 * Start the server.
	 */
	public static void main(String[] args) {
		if (System.getSecurityManager() == null) {
			System.setSecurityManager(new SecurityManager());
		}

		try {
            parseArgs(args);

			JobQueueServer server = 
					new SimpleJobQueueServer(numWorkers, maxDequeSize);
			JobQueueServer stub = 
			    (JobQueueServer) UnicastRemoteObject.exportObject(server, 0);
			Registry registry = LocateRegistry.getRegistry();
			registry.rebind("server", stub);
			System.out.println("Server bound. Workers: " + numWorkers +
					", Max Deque Size: " + maxDequeSize);

            /**
             * Lookup all the workers busy waiting until all of the expected
             * workers are online. 
             *
             * This step is done in main because otherwise we would need to
             * pass the registry to the `spawnWorkerThreads' function.
             */
			JobProcessingWorker[] workers = loadWorkers(numWorkers, registry);
			System.out.println("All workers have been bound.");

            /**
             * Spawn all the workers, taking care of monitoring.
             */
			Monitor monitor = new Monitor();
			int result = server.spawnWorkerThreads(workers, monitor);
			if (result != 0) {
				throw new RuntimeException("Server couldn't spawn worker threads ("
						+ result + ")");
			}
		} 
		catch (Exception e) {
			System.err.println("SimpleJobQueueServerRunner exception:");
			e.printStackTrace();
		}
	}
	
	/**
	 * Loads all workers from a RMI registry.
	 * @param numWorkers
	 * @param registry
	 * @return
	 * @throws AccessException
	 * @throws NotBoundException
	 * @throws RemoteException
	 */
	public static JobProcessingWorker[] loadWorkers(int numWorkers, Registry registry)
			throws AccessException, NotBoundException, RemoteException {
		JobProcessingWorker[] workers = new JobProcessingWorker[numWorkers]; 
		JobProcessingWorker current;
		int i = 0;

        while(i < numWorkers) {
		    current = (JobProcessingWorker) registry.lookup("worker-" + i);
		    
		    // Try to find this worker.
		    if (current != null) {
                workers[i++] = current;
            }
		    else {
		    	System.out.println("Worker " + i + " could not be found.");
		    }

		    // Sleep for a while and then go on with loading workers.
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                throw new RuntimeException("Main thread interrupted!", e);
            }
		}
        
        // Check whether all expected workers have been loaded.
        if (numWorkers != i) {
        	System.out.println("Notice: Number of workers reduced to " + i);

        	JobProcessingWorker[] workers2 = new JobProcessingWorker[i];
        	for (int j = 0; j < i; j++) {
        		workers2[j] = workers[j];
        	}
        	return workers2;
        }
        return workers;
	}

	public static void usage() {
	    System.out.println("Usage: java SimpleJobQueueServerRunner [options]");
	    System.out.println("\t -n <int>\t Set the number of workers.");
	    System.out.println("\t -m <int>\t Set the maximum queue size per worker.");
	    System.out.println("\n");
    }

    /**
     * Parse command-line arguments.
     *
     * Instead of mixing this code into the code in main, we use this function
     * to allow easy extension of the number of arguments.
     */
    public static void parseArgs(String[] args) {
        int argc = args.length,
            i    = 0;

        if (argc == 0) {
            usage();
        }
        
        for (; i < argc; i++) {
            if (args[i].equals("-n")) {
                try {
                    numWorkers = Integer.parseInt(args[++i]);
                } catch(NumberFormatException e) {
                    System.out.println(
                            "Number of workers must be a valid integer!"
                            );
                    usage();
                }
            } 
            else if(args[i].equals("-m")) {
                try {
                    maxDequeSize = Integer.parseInt(args[++i]);
                } catch(NumberFormatException e) {
                    System.out.println(
                            "Maximum queue size must be a valid integer!"
                            );
                    usage();
                }
            }
        }
    }
}
